Skip to content

Add safe context watcher API - #6227

Open
florentinl wants to merge 22 commits into
PyO3:mainfrom
florentinl:florentin.labelle/add-safe-wrappers-for-context-watching-api
Open

Add safe context watcher API#6227
florentinl wants to merge 22 commits into
PyO3:mainfrom
florentinl:florentin.labelle/add-safe-wrappers-for-context-watching-api

Conversation

@florentinl

@florentinl florentinl commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Adds PyContext and safe context-watcher bindings for GIL-enabled CPython 3.14 and newer:

  • typed context-switch events
  • bound and unbound RAII watcher guards
  • panic- and error-safe callback trampoline

PyContext itself is available on all supported CPython versions.

use pyo3::{
    prelude::*,
    types::{
        context::{watch_callback, ContextEvent},
        PyContext,
    },
};

fn context_changed(_py: Python<'_>, event: ContextEvent<'_, '_>) -> PyResult<()> {
    if let ContextEvent::Switched(context) = event {
        println!("current context: {context:?}");
    }
    Ok(())
}

fn main() -> PyResult<()> {
    Python::initialize();

    Python::attach(|py| {
        let _watcher = PyContext::add_watcher(py, watch_callback!(context_changed))?;
        py.run(
            c"import contextvars\ncontextvars.Context().run(lambda: None)",
            None,
            None,
        )
    })
}

Tested with unit tests, doctests, and Clippy on Python 3.14.

Comment thread src/context.rs Outdated
Comment thread src/context.rs Outdated
@florentinl
florentinl force-pushed the florentin.labelle/add-safe-wrappers-for-context-watching-api branch from eb0dff3 to 66ad857 Compare July 28, 2026 07:47
@florentinl
florentinl marked this pull request as ready for review July 28, 2026 08:19
@florentinl
florentinl requested a review from chirizxc July 28, 2026 08:19
Comment thread src/context.rs Outdated
@florentinl
florentinl force-pushed the florentin.labelle/add-safe-wrappers-for-context-watching-api branch from 7259b06 to 69b141c Compare July 28, 2026 10:40
@florentinl
florentinl requested a review from chirizxc July 28, 2026 11:20
@chirizxc

Copy link
Copy Markdown
Contributor

LGTM, but im not a maintainer

@florentinl

Copy link
Copy Markdown
Contributor Author

Still, thank you for taking a look.

@florentinl
florentinl force-pushed the florentin.labelle/add-safe-wrappers-for-context-watching-api branch 2 times, most recently from b253b69 to 2041732 Compare August 13, 2026 13:27
gh-worker-dd-mergequeue-cf854d Bot pushed a commit to DataDog/dd-trace-py that referenced this pull request Aug 13, 2026
## Description

Add a generic CPython 3.14+ publisher for the internal `python.context.switch` event.

It registers the CPython `PyContext_AddWatcher` API from the native extension and dispatches the event after each context switch.

The C callback catches Rust panics at the FFI boundary and preserves any exception already pending when CPython invokes it.

This PR is stacked on #19604, which adds the public `DD_TRACE_OTEL_CTX_ENABLED` configuration and activation-listener setup. The setting defaults to `true`; setting it to `false` disables both the listener and this Python 3.14 watcher. It also complements #19336, which independently provides the asyncio, uvloop, AnyIO, and greenlet emitters for older Python versions.

## Testing

- `scripts/lint checks`
- `reno lint`
- CPython 3.14 native context watcher: 4 passed
- Linux CPython 3.14.5 OTel thread context: 6 passed

## Risks

Low

## Additional Notes

- The PR contains raw external C bindings to CPython. I am currently upstreaming them to PyO3; once that work is merged and released, we can rely on the PyO3 bindings and simplify this code:
  - PyO3/pyo3#6204
  - PyO3/pyo3#6227


Co-authored-by: florentin.labelle <florentin.labelle@datadoghq.com>
@florentinl
florentinl force-pushed the florentin.labelle/add-safe-wrappers-for-context-watching-api branch 2 times, most recently from d0a8de6 to cf95e72 Compare August 20, 2026 13:32
@codspeed-hq

codspeed-hq Bot commented Aug 20, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 141 untouched benchmarks
⏩ 6 skipped benchmarks1


Comparing florentinl:florentin.labelle/add-safe-wrappers-for-context-watching-api (1f0fe5a) with main (ac9b689)

Open in CodSpeed

Footnotes

  1. 6 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@davidhewitt

Copy link
Copy Markdown
Member

I would quite like to avoid the macro for this API, it seems unfortunate. I understand why it's there, however. I'm thinking about what we can do as alternatives.

@davidhewitt davidhewitt left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I opened florentinl#1 which has a suggestion how I'd like this API to look.

Also placed a few other comments here.

Comment thread src/context.rs Outdated
Comment thread src/context.rs Outdated
Comment thread src/context.rs Outdated
Comment thread src/context.rs Outdated
@florentinl
florentinl force-pushed the florentin.labelle/add-safe-wrappers-for-context-watching-api branch from cf95e72 to e4ac1c4 Compare August 27, 2026 12:24
@florentinl

Copy link
Copy Markdown
Contributor Author

Thank you a lot @davidhewitt for the thorough review and your design suggestion.

I updated the PR to remove the macro API in favor of the design you suggested and tried to address your other comments.

@florentinl
florentinl requested a review from davidhewitt August 27, 2026 15:17

@davidhewitt davidhewitt left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this generally looks ready to me, just some small nits.

Comment thread src/types/context.rs Outdated
Comment thread src/types/context.rs
Comment thread src/types/context.rs Outdated
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-02T11:42:12.421778Z 9def052 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1a4afcdbe6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/types/context.rs
@florentinl
florentinl force-pushed the florentin.labelle/add-safe-wrappers-for-context-watching-api branch 2 times, most recently from a9e5201 to c0f278b Compare August 31, 2026 13:35
Comment thread src/types/context.rs
@florentinl
florentinl force-pushed the florentin.labelle/add-safe-wrappers-for-context-watching-api branch from c0f278b to 9def052 Compare September 2, 2026 11:38

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9def05278d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/types/context.rs
// attaching to more than one interpreter, so this is the interpreter for which the watcher
// was registered
// - `watcher_id` was returned by `PyContext_AddWatcher`
error_on_minusone(py, unsafe { ffi::PyContext_ClearWatcher(watcher_id) })

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Defer clearing watchers during callback dispatch

On GIL-enabled CPython 3.14.4, if an earlier watcher uses safe shared state to call clear on a later watcher, this immediately nulls the later callback slot while CPython is still iterating a snapshot of the active-watcher bitmask; CPython subsequently calls that null slot and segfaults. The drop path has the same issue because it also calls PyContext_ClearWatcher immediately, so the safe API permits memory-unsafe behavior. Track callback dispatch and defer removals until it completes (or otherwise prevent clearing another watcher from within a callback).

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really know how it should be handled in PyO3.
python/cpython#155943 which was opened to fix python/cpython#155619 solves the problem.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now I think we can call this a CPython bug and merge this as-is in PyO3; it's a silly edge case which I think we can allow to crash even if it's not safe.

@davidhewitt
davidhewitt added this pull request to the merge queue Sep 6, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 6, 2026
@davidhewitt

Copy link
Copy Markdown
Member

Looks like something still not properly skipped in the wasm tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants